package prickworlds.game;

import java.util.ArrayList;

import prickworlds.game.templates.MapTemplate;
import prickworlds.game.templates.WeaponTemplate;
import prickworlds.graphics.Explosion;
import prickworlds.graphics.ParticleEffect;
import prickworlds.structure.Drawable;
import static org.lwjgl.opengl.GL11.*;

/**
 * Represents a map which can be played on
 * 
 * @author Nicolas Winkler
 * 
 */
public class Map implements Drawable {

	/**
	 * the width and height of a single cell in pixels
	 */
	private int cellSize = 32;

	/**
	 * the unchangeable part of the map
	 * 
	 * this part is stored in a map file
	 */
	private MapTemplate template;

	/**
	 * the player robot
	 */
	private Player player;

	/**
	 * contains all the bullets
	 */
	private ArrayList<Bullet> bullets;

	/**
	 * contains the enemies
	 */
	private ArrayList<EnemyRobot> enemies;

	private ArrayList<DropItem> drops;

	private ArrayList<ParticleEffect> particleEffects;

	/**
	 * width of the map in fields
	 */
	private int width;

	/**
	 * height of the map in fields
	 */
	private int height;

	/**
	 * a two-dimensional array of walls containing the positions of walls.
	 */
	private Wall[][] wallMap;

	/**
	 * a list containing the same walls as {@link wallMap} but in list form.
	 */
	private ArrayList<Wall> walls;

	private int nextWallTextureChange;

	/**
	 * initializes the map
	 */
	public Map(MapTemplate template) {
		this.template = template;
		setPlayer(new Player(template.getRobotTemplate(1)));
		getPlayer().posX = 5f;
		getPlayer().posY = 5f;
		getPlayer().team = 0;

		setBullets(new ArrayList<Bullet>());
		setDrops(new ArrayList<DropItem>());
		setEnemies(new ArrayList<EnemyRobot>());

		walls = new ArrayList<Wall>();
		particleEffects = new ArrayList<ParticleEffect>();

		this.width = template.getWidth();
		this.height = template.getHeight();

		wallMap = new Wall[width][height];

		for (int i = 0; i < width; i++) {
			addWall(i, 0);
			addWall(i, height - 1);
		}

		for (int i = 1; i < height - 1; i++) {
			addWall(0, i);
			addWall(width - 1, i);
		}
		updateWallTextures();
	}

	public void updateWallTextures() {
		for (int i = 0; i < width; i++) {
			for (int j = 0; j < height; j++) {
				Wall w = wallMap[i][j];

				if (w != null) {
					boolean left = i > 0 && wallMap[i - 1][j] != null;
					boolean right = i < wallMap.length - 1
							&& wallMap[i + 1][j] != null;
					boolean up = j > 0 && wallMap[i][j - 1] != null;
					boolean down = j < wallMap[i].length - 1
							&& wallMap[i][j + 1] != null;

					if (left && right && j < height / 2)
						w.setTextures(template.createWallTextures(0));
					else if (left && right && j >= height / 2)
						w.setTextures(template.createWallTextures(2));
					else if (up && down && i < width / 2)
						w.setTextures(template.createWallTextures(1));
					else if (up && down && i >= width / 2)
						w.setTextures(template.createWallTextures(3));

					else if (up && right)
						w.setTextures(template.createWallTextures(4));
					else if (down && right)
						w.setTextures(template.createWallTextures(5));
					else if (down && left)
						w.setTextures(template.createWallTextures(6));
					else if (up && left)
						w.setTextures(template.createWallTextures(7));

					else
						w.setTextures(template.createWallTextures(9));
				}
			}
		}
	}

	@Override
	public void display() {

		glPushMatrix();
		glScalef(getCellSize(), getCellSize(), 1f);
		for (int i = 0; i < walls.size(); i++) {
			Wall w = walls.get(i);
			w.display();
		}

		for (int i = 0; i < drops.size(); i++) {
			DropItem d = drops.get(i);
			d.display();
		}

		for (int i = 0; i < getBullets().size(); i++) {
			Bullet bullet = getBullets().get(i);
			bullet.display();
		}

		for (int i = 0; i < getEnemies().size(); i++) {
			EnemyRobot ir = getEnemies().get(i);
			ir.display();
		}

		getPlayer().display();

		for (int i = 0; i < particleEffects.size(); i++) {
			particleEffects.get(i).display();
		}

		glPopMatrix();
	}

	@Override
	public void update() {
		nextWallTextureChange--;
		if (nextWallTextureChange <= 0) {
			if (Wall.getUsedTexture() == 0) {
				Wall.setUsedTexture(1);
			} else {
				Wall.setUsedTexture(0);
			}
			nextWallTextureChange = 30;
		}

		for (int i = 0; i < walls.size(); i++) {
			Wall a = walls.get(i);
			for (int j = i + 1; j < walls.size(); j++) {
				Wall b = walls.get(j);
				a.processCollision(b);
			}
		}

		for (int i = 0; i < drops.size(); i++) {
			DropItem di = drops.get(i);
			di.update();
			for (int j = 0; j < enemies.size(); j++) {
				Robot p = enemies.get(j);
				if (di.getHitbox().overlaps(p.getHitbox())) {
					di.foundBy(p);
				}
			}
			if (di.getHitbox().overlaps(player.getHitbox())) {
				di.foundBy(player);
			}
			if (di.isFinished()) {
				drops.remove(i);
				i--;
				continue;
			}
		}

		for (int i = 0; i < particleEffects.size(); i++) {
			particleEffects.get(i).update();
			if (particleEffects.get(i).isFinished()) {
				particleEffects.remove(i);
				i--;
				continue;
			}
		}
	}

	public void addWall(int x, int y) {
		Wall wall = new Wall(x, y, this);
		walls.add(wall);
		wallMap[x][y] = wall;
	}

	public void addExplosion(float x, float y) {
		addExplosion(x, y, 1.0f);
	}

	public void addExplosion(float x, float y, float size) {
		particleEffects.add(new Explosion(x, y, size));
	}

	/**
	 * @param shooter
	 *            the {@link Robot} that emits the shot
	 * @param targetX
	 *            the x-coordinate of the target
	 * @param targetY
	 *            the y-coordinate of the target
	 */
	public void createBullet(Robot shooter, WeaponTemplate weapon,
			float targetX, float targetY, int team) {

		Bullet bullet = new Bullet(weapon);

		bullet.posX = shooter.posX;
		bullet.posY = shooter.posY;

		bullet.team = team;

		float x = targetX - shooter.posX;
		float y = targetY - shooter.posY;

		float shortener = 0.01f / (float) Math.sqrt(x * x + y * y);

		x *= shortener;
		y *= shortener;

		bullet.velX = x * bullet.speed;
		bullet.velY = y * bullet.speed;

		bullet.accX = x * bullet.acceleration;
		bullet.accY = y * bullet.acceleration;

		bullets.add(bullet);
	}

	public void setPlayer(Player player) {
		this.player = player;
	}

	public Player getPlayer() {
		return player;
	}

	public void setBullets(ArrayList<Bullet> bullets) {
		this.bullets = bullets;
	}

	public ArrayList<Bullet> getBullets() {
		return bullets;
	}

	public void setEnemies(ArrayList<EnemyRobot> enemies) {
		this.enemies = enemies;
	}

	public ArrayList<EnemyRobot> getEnemies() {
		return enemies;
	}

	public void setCellSize(int cellSize) {
		this.cellSize = cellSize;
	}

	public int getCellSize() {
		return cellSize;
	}

	public MapTemplate getTemplate() {
		return template;
	}

	public ArrayList<Wall> getWalls() {
		return walls;
	}

	public void setWalls(ArrayList<Wall> walls) {
		this.walls = walls;
	}

	public int getWidth() {
		return width;
	}

	public void setWidth(int width) {
		this.width = width;
	}

	public int getHeight() {
		return height;
	}

	public void setHeight(int height) {
		this.height = height;
	}

	public ArrayList<DropItem> getDrops() {
		return drops;
	}

	public void setDrops(ArrayList<DropItem> drops) {
		this.drops = drops;
	}
}
